home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / RTF / view.C < prev    next >
C/C++ Source or Header  |  1994-08-01  |  12KB  |  651 lines

  1. /* $Header: /usr/people/pcd/Src/RTF/RCS/view.C,v 1.1 92/11/23 12:58:56 pcd Exp Locker: pcd $
  2.  */
  3.  
  4. #include "view.h"
  5.  
  6. #include <assert.h>
  7. #include <string.h>
  8. #include "debug.h"
  9.  
  10. int debug_rtf = 0;
  11. int debug_rtfall = 0;
  12.  
  13. class RTFParagraphFormat : public ParagraphFormat{
  14. public:
  15.   void set_attr(int, int);
  16.  
  17.   Inches li(int twips)
  18.     { return left_indent = (double)twips / 1440; };
  19.  
  20.   Inches ri(int twips)
  21.     { return right_indent = (double)twips / 1440; };
  22.  
  23.   Inches fi(int twips)
  24.     { return first_indent = (double)twips / 1440; };
  25.  
  26.   Inches sb(int twips)
  27.     { return space_before = (double)twips / 1440; };
  28.  
  29.   Inches sa(int twips)
  30.     { return space_after = (double)twips / 1440; };
  31.  
  32.   Inches sl(int twips)
  33.     { return space_between = (double)twips / 1440; };
  34.  
  35.   static void deftab(int twips)
  36.     { default_tab_width((double)twips / 1440); };
  37.  
  38.   void tx(int twips)
  39.     { add_tab_stop((double)twips / 1440 + left_indent); };
  40.  
  41. };
  42.  
  43.  
  44. void
  45. ParagraphFormat::normal()
  46. {
  47.   first_indent = left_indent = right_indent = 0;
  48.   space_before = space_after = space_between = 0;
  49.   tabqty = 0;
  50.   justification = quad_left;
  51. }
  52.  
  53. void
  54. RTFParagraphFormat::set_attr(int attr, int val)
  55. {
  56.   switch(attr){
  57.   case RTF::ParDef:
  58.     normal();
  59.     break;
  60.  
  61.   case RTF::QuadLeft:
  62.     justification = quad_left;
  63.     break;
  64.  
  65.   case RTF::QuadRight:
  66.     justification = quad_right;
  67.     break;
  68.  
  69.   case RTF::QuadJust:
  70.     justification = quad_full;
  71.     break;
  72.  
  73.   case RTF::QuadCenter:
  74.     justification = quad_center;
  75.     break;
  76.  
  77.   case RTF::CellPos: /* @@ table hack */
  78.   case RTF::TabPos:
  79.     tx(val);
  80.     break;
  81.  
  82.   case RTF::SpaceBefore:
  83.     sb(val);
  84.     break;
  85.  
  86.   case RTF::SpaceAfter:
  87.     sa(val);
  88.     break;
  89.  
  90.   case RTF::SpaceBetween:
  91.     sl(val);
  92.     break;
  93.  
  94.   case RTF::LeftIndent:
  95.     li(val);
  96.     break;
  97.  
  98.   case RTF::RightIndent:
  99.     ri(val);
  100.     break;
  101.  
  102.   case RTF::FirstIndent:
  103.     fi(val);
  104.     break;
  105.  
  106.     //@@ others!
  107.   }
  108. }
  109.  
  110. void
  111. RTFCharacterFormat::set_attr(int attr, int val)
  112. {
  113.   switch(attr){
  114.   case RTF::Plain:
  115.     plain();
  116.     break;
  117.  
  118.   case RTF::Bold:
  119.     bold = val;
  120.     break;
  121.  
  122.   case RTF::Italic:
  123.     italic = val;
  124.     break;
  125.  
  126.   case RTF::Underline:
  127.     /* Microsoft RTF never uses \ul0
  128.        but I'm supporting it because NeXT rtf files do.
  129.      */
  130.     underline = val;
  131.     break;
  132.  
  133.   case RTF::NoUnderline:
  134.     underline = 0;
  135.     break;
  136.  
  137.   case RTF::FontFamily:
  138.     font_family = val;
  139.     break;
  140.  
  141.   case RTF::SuperScript:
  142.     super = val;
  143.     break;
  144.  
  145.   case RTF::SubScript:
  146.     sub = val;
  147.     break;
  148.  
  149. #ifdef FONT_SIZES
  150.   case RTF::FontSize:
  151.     size = val;
  152.     break;
  153. #endif
  154.  
  155.   case RTF::ForeColor:
  156.     foreground = val;
  157.     break;
  158.   }
  159. }
  160.  
  161. View::View()
  162. {
  163.   flow_ = 0;
  164.   page_ = 0;
  165.   first_ = last_ = 0;
  166. }
  167.  
  168. View::~View()
  169. {
  170.   delete flow_;
  171.   delete page_;
  172. }
  173.  
  174.  
  175. int
  176. View::string(const char* d, int rtf)
  177. {
  178.   delete page_;
  179.   page_ = 0;
  180.   delete flow_;
  181.   flow_ = 0;
  182.  
  183.   Qty q;
  184.   if(d == 0 || (q = strlen(d)) == 0)
  185.     return 1;
  186.  
  187.   if(rtf)
  188.     flow_ = RichText(d, q);
  189.   else
  190.     flow_ = ASCII(d, q);
  191.  
  192.   first_ = 0;
  193.   last_ = flow_ ? flow_->bytes() : 0;
  194.   clear();
  195.  
  196.   return flow_ ? 1 : 0;
  197. }
  198.  
  199.  
  200. int
  201. View::format()
  202. {
  203.   if(!flow_) return 0;  /* must be text available */
  204.  
  205.   if(page_) /* already formatted */
  206.     return 1;
  207.  
  208.   page_ = new TextRect();
  209.  
  210.   page_->format(flow_, first(), last(), bounds_);
  211.   /* page_->bounds().top() is end of formatted area */
  212.   bounds_.height(page_->bounds().top() - bounds_.top());
  213.   return 1;
  214. }
  215.  
  216. int
  217. View::resize(BRect b)
  218. {
  219.   if(bounds_ == b)
  220.     return 0;
  221.  
  222.   bounds_ = b;
  223.  
  224.   unformat();
  225.   return 1;
  226. }
  227.  
  228. void
  229. View::unformat()
  230. {
  231.   if(page_){
  232.     delete page_;
  233.     page_ = 0;
  234.  
  235.     clear();
  236.   }
  237. }
  238.  
  239.  
  240. int
  241. View::range(TextPosition f, TextPosition l)
  242. {
  243.   if(f == first() && l == last())
  244.     return 0;
  245.  
  246.   first_ = f;
  247.   last_ = l;
  248.  
  249.   unformat();
  250.   return 1;
  251. }
  252.  
  253.  
  254. TextFlow*
  255. View::ASCII(const char* data, Qty q)
  256. {
  257.   TextFlow* ret = new TextFlow((const Byte*)data, q);
  258.  
  259.   TextPosition first, last;
  260.   CharacterFormat plain;
  261.   plain.fg_pixel = color(0,0,0); //@@ should be in CharacterFormat::plain()
  262.  
  263.   char* p;
  264.  
  265.   for(first = 0, p=(char*)data;
  266.       first < q && (p = strchr(p, '\n'));
  267.       p++, first=last+1){
  268.     last = p - (char*)data;
  269.     if(last>first)
  270.       text_flow(*ret, first, last, plain);
  271.     new_line(*ret, last, last+1, 0);
  272.   }
  273.  
  274.   if(first < q)
  275.     text_flow(*ret, first, q, plain);
  276.  
  277.   return ret;
  278. }
  279.  
  280.  
  281. class IntTable {
  282. public:
  283.   IntTable(IntTable* s, int indx, int elt)
  284.     { link_ = s; indx_ = indx; elt_ = elt; };
  285.  
  286.   ~IntTable()
  287.     { delete link_; }
  288.  
  289.   int element(int indx)
  290.     // return 0 on not found
  291.     { return indx_ == indx ? elt_ : link_ ? link_->element(indx) : 0;};
  292.  
  293. private:
  294.   int elt_, indx_;
  295.   IntTable* link_;
  296. };
  297.  
  298. class ColorSequence {
  299. public:
  300.   ColorSequence(ColorSequence* s, unsigned long p)
  301.     { link_ = s; pixel_ = p; };
  302.  
  303.   ~ColorSequence()
  304.     { delete link_; }
  305.  
  306.   ColorSequence* reverse(ColorSequence* prev=0)
  307.     { ColorSequence* l = link_;
  308.       link_ = prev;
  309.       return l ? l->reverse(this) : this;
  310.     };
  311.  
  312.   unsigned long element(int indx)
  313.     // return 0 on not found
  314.     { return indx == 0 ? pixel_
  315.     : link_ ? link_->element(indx-1) : 0; };
  316.  
  317. private:
  318.   unsigned long pixel_;
  319.   ColorSequence* link_;
  320. };
  321.  
  322.  
  323. static IntTable*
  324. parse_font_table(RTFPos& here)
  325. {
  326.   IntTable* ret = 0;
  327.   int fnum = 0; // appease compiler.
  328.  
  329.   int braces = 1;
  330.   do{
  331.     here.GetToken();
  332.     if(here.CheckCM(RTF::Control, RTF::FontFamily))
  333.       ret = new IntTable(ret, fnum, here.Minor() - RTF::FFNil);
  334.     else if(here.CheckCMM(RTF::Control, RTF::CharAttr, RTF::FontNum))
  335.       fnum = here.Param();
  336.     else if(here.CheckCM(RTF::Group, RTF::BeginGroup))
  337.       braces++;
  338.     else if(here.CheckCM(RTF::Group, RTF::EndGroup))
  339.       braces--;
  340.   }while(braces>0);
  341.   return ret;
  342. }
  343.  
  344. static ColorSequence*
  345. parse_color_table(RTFPos& here, const View& view)
  346. {
  347.   ColorSequence* ret = 0;
  348.   unsigned char r, g, b;
  349.  
  350.   int braces = 1;
  351.   do{
  352.     here.GetToken();
  353.     if(here.CheckCM(RTF::Control, RTF::ColorName))
  354.       switch(here.Minor()){
  355.       case RTF::Red:
  356.     r = here.Param();
  357.     break;
  358.  
  359.       case RTF::Green:
  360.     g = here.Param();
  361.     break;
  362.  
  363.       case RTF::Blue:
  364.     b = here.Param();
  365.     ret = new ColorSequence(ret, view.color(r<<8, g<<8, b<<8));
  366.     break;
  367.  
  368.       }
  369.     else if(here.CheckCM(RTF::Group, RTF::BeginGroup))
  370.       braces++;
  371.     else if(here.CheckCM(RTF::Group, RTF::EndGroup))
  372.       braces--;
  373.   }while(braces>0);
  374.   return ret ? ret->reverse() : 0;
  375. }
  376.  
  377. TextFlow*
  378. View::RichText(const char* data, Qty q)
  379. {
  380.   assert(data && q>0);
  381.  
  382.   RTFPos token(data);
  383.  
  384.   token.GetToken();
  385.   REQUIRE(token.CheckCM(RTF::Group, RTF::BeginGroup), return 0);
  386.  
  387.   TextFlow* parent_group = new TextFlow((const Byte*)data, q);
  388.  
  389.   RTFParagraphFormat::deftab(720);
  390.   RTFParagraphFormat pgf_fmt;
  391.   RTFCharacterFormat* char_fmt = new RTFCharacterFormat(0);
  392.   IntTable* font_table = 0;
  393.   ColorSequence* color_table = 0;
  394.  
  395.   TextPosition start_paragraph=0;
  396.  
  397.   int braces = 1;
  398.  
  399.   token.GetToken();
  400.  
  401.   while(braces > 0 && token.start() < q){
  402.     Debug(rtfall, ("RTF parse token: '%s'\n", token.TokenText()));
  403.     switch(token.Class()){
  404.     case RTF::Group:
  405.       if(token.Major() == RTF::BeginGroup){
  406.     char_fmt = new RTFCharacterFormat(char_fmt);
  407.     braces++;
  408.       }else{
  409.     char_fmt = char_fmt->pop();
  410.     braces--;
  411.       }
  412.       break;
  413.  
  414.     case RTF::Text:
  415.       //@@ fg_pixel should be maintained by RTFCharacterFormat
  416.       char_fmt->fg_pixel = color_table ?
  417.     color_table->element(char_fmt->foreground)
  418.       : color(0,0,0);
  419.  
  420.       if(token.Minor()){
  421.     text_char(*parent_group, token.start(), token.end(), token.Major(),
  422.           *char_fmt);
  423.       }else{
  424.     parse_text(token, *parent_group, *char_fmt);
  425.     continue;
  426.       }
  427.       break;
  428.  
  429.     case RTF::Control:
  430.       switch(token.Major()){
  431.  
  432.       case RTF::DocAttr:
  433.     switch(token.Minor()){
  434.     case RTF::DefTab:
  435.       RTFParagraphFormat::deftab(token.Param());
  436.       break;
  437.     }
  438.     break;
  439.  
  440.       case RTF::CharAttr:
  441.     if(token.Minor() == RTF::Invisible)
  442.       token.SkipGroup();
  443.     else if(token.Minor() == RTF::FontNum){
  444.       if(font_table)
  445.         // element returns 0, i.e. fnil on errors
  446.         char_fmt->set_attr(RTF::FontFamily,
  447.                    font_table->element(token.Param()));
  448.     }
  449.     else
  450.       char_fmt->set_attr(token.Minor(), token.Param());
  451.     break;
  452.  
  453.       case RTF::ParAttr:
  454.     pgf_fmt.set_attr(token.Minor(), token.Param());
  455.     break;
  456.     
  457.       case RTF::SpecialChar:
  458.     switch(token.Minor()){
  459.       extern int debug_tabs;
  460.     case RTF::Tab:
  461.     case RTF::Cell: /* @@ hack to make sense of tables */
  462.       new TabFlow(*parent_group, token.start(), token.end(),
  463.               pgf_fmt, *this);
  464.       break;
  465.  
  466.     case RTF::Line:
  467.     case RTF::Row: /* @@ hack to make sense of tables */
  468.       new_line(*parent_group, token.start(), token.end(), 0);
  469.       break;
  470.       
  471.     case RTF::Page: //@#
  472.     case RTF::Par:
  473.       //@@ use char format for height of newline
  474.       new_line(*parent_group, token.start(), token.end(),
  475.            pgf_fmt.space_after);
  476.       new ParagraphFlow(*parent_group, start_paragraph, token.end(),
  477.             pgf_fmt, *this);
  478.       start_paragraph = token.end();
  479.       break;
  480.     }
  481.     break;
  482.     
  483.       case RTF::Destination:
  484.     switch(token.Minor()){
  485.     case RTF::Field:
  486.     case RTF::FieldResult:
  487.     case RTF::Index:
  488.       break;
  489.  
  490.     case RTF::FontTbl:
  491.       font_table = parse_font_table(token);
  492.       char_fmt = char_fmt->pop();
  493.       braces--;
  494.       break;
  495.  
  496.     case RTF::ColorTbl:
  497.       color_table = parse_color_table(token, *this);
  498.       char_fmt = char_fmt->pop();
  499.       braces--;
  500.       break;
  501.  
  502.     case RTF::Pict:
  503.       parse_picture(token, *parent_group);
  504.       /* and skipgroup... */
  505.     default:
  506.       token.SkipGroup();
  507.       char_fmt = char_fmt->pop();
  508.       braces--;
  509.       break;
  510.     }
  511.       }
  512.     }
  513.     token.GetToken();
  514.   }
  515.   return parent_group;
  516. }
  517.  
  518.  
  519. void
  520. RTFPos::getText()
  521. {
  522.   size_t len = strcspn(p, "\t\n\r\\{}");
  523.   end_+= len;
  524.   p+= len;
  525. }
  526.  
  527.  
  528. void
  529. RTFPos::getHex()
  530. {
  531.   size_t len = strcspn(p, "\\{}");
  532.   end_+= len;
  533.   p+= len;
  534. }
  535.  
  536.  
  537. void
  538. View::parse_text(RTFPos& token, TextFlow& parent, const CharacterFormat& cf)
  539. {
  540.   TextPosition start = token.start();
  541.  
  542.   token.getText();
  543.   Debug(rtf, ("word(%d,%d)\n", start, token.start()));
  544.   text_flow(parent, start, token.end(), cf);
  545.   token.GetToken();
  546. }
  547.  
  548.  
  549. void
  550. View::parse_picture(RTFPos& token, TextFlow& parent_group)
  551. {
  552.   TextPosition start = -1;
  553.   Coord height=-1, width=-1;
  554.  
  555.   do{
  556.     if(token.CheckCM(RTF::Control, RTF::PictAttr)){
  557.       switch(token.Minor()){
  558.       case RTF::PicWid:
  559.     width = token.Param();
  560.     break;
  561.       case RTF::PicHt:
  562.     height = token.Param();
  563.     break;
  564.     /* @# others? bitmap type? */
  565.       }
  566.     }else
  567.       if(token.Class() == RTF::Text){
  568.     start = token.start();
  569.     token.getHex();
  570.       }
  571.     token.GetToken();
  572.   }while(token.Class() != RTF::Group && token.Class() != RTF::Eof);
  573.  
  574.   if(height > 0 && width > 0 && token.start() > start)
  575.     /* all color info must be in the hex data.
  576.      * right now, SunRaster/RLE (1 or 8 bit) is used
  577.      * @@ pass a bitmap type?
  578.      */
  579.     picture(parent_group, start, token.start(), width, height);
  580. }
  581.   
  582.  
  583. char*
  584. View::plain_text(const char* rich_text, Qty q)
  585. {
  586.   RTFPos token(rich_text);
  587.   char *ret = new char[q+1];
  588.   char *dest = ret;
  589.  
  590.   token.GetToken();
  591.  
  592.   while(token.start() < q){
  593.     switch(token.Class()){
  594.     case RTF::Group:
  595.       break;
  596.  
  597.     case RTF::Text:
  598.       *dest++ = token.Major();
  599.       break;
  600.  
  601.     case RTF::Control:
  602.       switch(token.Major()){
  603.  
  604.       case RTF::CharAttr:
  605.     if(token.Minor() == RTF::Invisible)
  606.       token.SkipGroup();
  607.     break;
  608.  
  609.       case RTF::ParAttr:
  610.     break;
  611.     
  612.       case RTF::SpecialChar:
  613.     switch(token.Minor()){
  614.     case RTF::Tab:
  615.       *dest++ = '\t';
  616.       break;
  617.  
  618.     case RTF::Line:
  619.       *dest++ = '\n';
  620.       break;
  621.       
  622.     case RTF::Page:
  623.       *dest++ = '\f';
  624.       break;
  625.  
  626.     case RTF::Par:
  627.       *dest++ = '\n';
  628.       *dest++ = '\n';
  629.       break;
  630.     }
  631.     break;
  632.     
  633.       case RTF::Destination:
  634.     switch(token.Minor()){
  635.     case RTF::Field:
  636.     case RTF::FieldResult:
  637.     case RTF::Index:
  638.       break;
  639.  
  640.     default:
  641.       token.SkipGroup();
  642.       break;
  643.     }
  644.       }
  645.     }
  646.     token.GetToken();
  647.   }
  648.   *dest++ = 0;
  649.   return ret;
  650. }
  651.